/* eslint-disable no-param-reassign */
/* eslint-disable @typescript-eslint/explicit-function-return-type */
import {
  useUIStore,
  useNamespace,
  IBizCustomRender,
  useControlController,
  hasEmptyPanelRenderer,
} from '@ibiz-template/vue3-util';
import {
  h,
  ref,
  watch,
  VNode,
  PropType,
  computed,
  nextTick,
  renderSlot,
  onUnmounted,
  defineComponent,
  resolveComponent,
  VNodeArrayChildren,
} from 'vue';
import { IDEGrid, IDEGridColumn, IDEGridGroupColumn } from '@ibiz/model-core';
import {
  ScriptFactory,
  GridController,
  IControlProvider,
} from '@ibiz-template/runtime';
import { NOOP } from '@ibiz-template/core';
import { recursiveIterate } from '@ibiz-template/core';
import { createUUID } from 'qx-util';
import {
  IGridProps,
  useAppGridBase,
  useITableEvent,
  useGridDraggable,
  useGridHeaderStyle,
} from './grid-control.util';
import { useRowEditPopover } from './use-row-edit-popover';
import { usePagination } from './util';
import { OdooGroupGridController } from './odoo-group-grid.controller';
import { IGroupData } from './interface';
import './odoo-group-grid.scss';

/**
 * 绘制成员的attrs
 * @author lxm
 * @date 2024-03-19 03:48:00
 * @param {IDEFormDetail} model
 * @return {*}  {IParams}
 */
function renderAttrs(model: IDEGridColumn | IDEGrid, params: IParams): IParams {
  const attrs: IParams = {};
  model.controlAttributes?.forEach(item => {
    // 表格合并行列应该排除由表格内置合并单元格方法处理
    if (item.attrName !== 'span-method') {
      if (item.attrName && item.attrValue) {
        attrs[item.attrName!] = ScriptFactory.execSingleLine(item.attrValue!, {
          ...params,
        });
      }
    }
  });
  return attrs;
}

// 绘制除分组列之外的表格列
export function renderColumn(
  c: GridController,
  model: IDEGridColumn,
  renderColumns: IDEGridColumn[],
  index: number,
): VNode | null {
  const { codeName: columnName, width } = model;

  // 查缓存，有缓存用缓存，没缓存的用模型
  const columnC = c.columns[columnName!];
  const columnState = c.state.columnStates.find(
    item => item.key === columnName,
  )!;

  // 如果没有配置自适应列，则最后一列变为自适应列
  const widthFlexGrow =
    columnC.isAdaptiveColumn ||
    (!c.hasAdaptiveColumn && index === renderColumns.length - 1);

  const widthName = widthFlexGrow ? 'min-width' : 'width';

  const tempWidth = columnState?.columnWidth || width;
  // 表格列自定义
  return (
    <el-table-column
      className={`${model.columnType?.toLowerCase()} ${model.columnType?.toLowerCase()}-${columnName}`}
      label={model.caption}
      prop={columnName}
      {...{ [widthName]: tempWidth }}
      fixed={columnState.fixed}
      sortable={
        model.enableSort ? c.model.sortMode === 'LOCAL' || 'custom' : false
      }
      sortMethod={(a: IData, b: IData) => {
        const fieldName = model.id!.toLowerCase();
        if (a[fieldName] < b[fieldName] || !a[fieldName]) return -1;
        if (a[fieldName] > b[fieldName] || !b[fieldName]) return 1;
        return 0;
      }}
      align={model.align?.toLowerCase() || 'center'}
    >
      {{
        header: ({ column }: IData) => {
          return (
            <iBizGridColumnHeader key={column.property} controller={columnC} />
          );
        },
        default: ({ row, column }: IData): VNode | null => {
          if (row.hasChildren) {
            if (column.no === 1)
              return (
                <div class='ibiz-grid-field-column'>
                  <span class='ibiz-grid-field-column__text'>{`${row.caption}（${row.count}）`}</span>
                </div>
              );
            if (model.aggField)
              return (
                <div class='ibiz-grid-field-column'>
                  <span class='ibiz-grid-field-column__text'>{`${row[model.aggField]}`}</span>
                </div>
              );
            return null;
          }
          const rowState = c.findRowState(row);
          if (rowState) {
            const comp = resolveComponent(c.providers[columnName!].component);
            return h(comp, {
              controller: columnC,
              row: rowState,
              key: row.tempsrfkey + columnName,
              attrs: renderAttrs(model, {
                ...c.getEventArgs(),
                data: rowState.data,
              }),
            });
          }
          return null;
        },
      }}
    </el-table-column>
  );
}

// 绘制表格列
export function renderChildColumn(
  c: GridController,
  model: IDEGridColumn,
  renderColumns: IDEGridColumn[],
  index: number,
): VNode | null {
  if (model.columnType === 'GROUPGRIDCOLUMN') {
    const childColumns =
      (model as IDEGridGroupColumn).degridColumns?.filter(
        item => !item.hideDefault && !item.hiddenDataItem,
      ) || [];
    const { width, codeName } = model;
    const columnC = c.columns[codeName!];
    const align = model.align?.toLowerCase() || 'center';
    return (
      <el-table-column
        prop={model.codeName}
        label={model.caption}
        min-width={width}
        align={align}
      >
        {{
          header: ({ column }: IData) => {
            return (
              <iBizGridColumnHeader
                key={column.property}
                controller={columnC}
              />
            );
          },
          default: (): VNodeArrayChildren => {
            return childColumns.map((column, index2) => {
              return renderChildColumn(c, column, renderColumns, index2);
            });
          },
        }}
      </el-table-column>
    );
  }
  return renderColumn(c, model, renderColumns, index);
}

export const OdooGroupGridControl = defineComponent({
  name: 'IBizOdooGroupGridControl',
  props: {
    /**
     * @description 表格模型数据
     */
    modelData: { type: Object as PropType<IDEGrid>, required: true },
    /**
     * @description 应用上下文对象
     */
    context: { type: Object as PropType<IContext>, required: true },
    /**
     * @description 视图参数对象
     * @default {}
     */
    params: { type: Object as PropType<IParams>, default: () => ({}) },
    /**
     * @description 部件适配器
     */
    provider: { type: Object as PropType<IControlProvider> },
    /**
     * @description 部件行数据默认激活模式，值为0:不激活，值为1：单击激活，值为2：双击激活
     */
    mdctrlActiveMode: { type: Number, default: undefined },
    /**
     * @description 是否单选
     */
    singleSelect: { type: Boolean, default: undefined },
    /**
     * @description 是否启用行编辑
     */
    rowEditOpen: { type: Boolean, default: undefined },
    /**
     * @description 是否是简单模式，即直接传入数据，不加载数据
     */
    isSimple: { type: Boolean, required: false },
    /**
     * @description 简单模式下传入的数据
     */
    data: { type: Array<IData>, required: false },
    /**
     * @description 是否默认加载数据
     * @default true
     */
    loadDefault: { type: Boolean, default: true },
  },
  setup(props, { slots }) {
    const c: OdooGroupGridController =
      useControlController<OdooGroupGridController>(
        (...args) => new OdooGroupGridController(...args),
      );
    const ns = useNamespace(`control-${c.model.controlType!.toLowerCase()}`);
    const odooNs = useNamespace(`odoo-group-grid`);

    const { zIndex } = useUIStore();
    c.state.zIndex = zIndex.increment();

    const { sysCss } = c.model;
    const sysCssName = sysCss?.cssName;

    const {
      tableRef,
      onRowClick,
      onDbRowClick,
      onSelectionChange,
      onSortChange,
      handleRowClassName,
      handleHeaderCellClassName,
      cleanClick = NOOP,
      cleanEnter = NOOP,
      cleanTab = NOOP,
    } = useITableEvent(c);
    const { onPageChange, onPageRefresh, onPageSizeChange } = usePagination(c);

    const { headerCssVars } = useGridHeaderStyle(tableRef, ns);

    const { cleanup = NOOP, setDragEvent } = useGridDraggable(tableRef, ns, c);

    const {
      tableData,
      renderColumns,
      defaultSort,
      summaryMethod,
      spanMethod,
      headerDragend,
    } = useAppGridBase(c, props as IGridProps, tableRef);

    const { renderPopover } = useRowEditPopover(tableRef, c);
    c.evt.on('onLoadSuccess', () => {
      if (setDragEvent) setDragEvent();
    });

    // 是否可以加载更多
    const isLodeMoreDisabled = computed(() => {
      if (c.model.pagingMode !== 2) {
        return true;
      }
      return (
        c.state.items.length >= c.state.total ||
        c.state.isLoading ||
        c.state.total <= c.state.size
      );
    });

    // 无限滚动元素
    const infiniteScroll = ref<IData>();
    // 无限滚动元素标识
    const infiniteScrollKey = ref<string>(createUUID());

    const isAllSelected = computed(() => {
      const { expandItems, selectedData } = c.state;
      if (!selectedData.length) return false;
      if (!expandItems.length) return true;
      const keySet = Array.from(new Set(selectedData.map(item => item.srfkey)));
      return expandItems.every(item => keySet.includes(item.srfkey));
    });

    const isSelected = (row: IData) => {
      return c.state.selectedData.some(item => item.srfkey === row.srfkey);
    };

    const toggleRowSelection = (row: IData) => {
      const { expandItems } = c.state;
      // 获取当前选中的展开项
      const selectedData = c.state.selectedData.filter(item =>
        expandItems.some(_item => item.srfkey === _item.srfkey),
      );
      const index = selectedData.findIndex(item => item.srfkey === row.srfkey);
      if (index !== -1) {
        selectedData.splice(index, 1);
      } else {
        selectedData.push(row);
      }
      c.setSelection(selectedData);
      // 单独某一项选中状态改变则退出数据全选状态
      c.handleSelectAllChange(false);
    };

    const toggleAllSelection = () => {
      const { expandItems } = c.state;
      const state = !isAllSelected.value;
      c.setSelection(state ? expandItems : []);
      if (state) c.handleSelectAllChange(!expandItems.length);
    };

    onUnmounted(() => {
      zIndex.decrement();
      if (cleanup !== NOOP) cleanup();
      if (cleanClick !== NOOP) cleanClick();
      if (cleanEnter !== NOOP) cleanEnter();
      if (cleanTab !== NOOP) cleanTab();
    });

    watch(
      () => c.state.curPage,
      () => {
        if (
          c.state.curPage === 1 &&
          (c.model.pagingMode === 2 || c.model.pagingMode === 3)
        ) {
          infiniteScrollKey.value = createUUID();
          const containerEl =
            infiniteScroll.value?.ElInfiniteScroll?.containerEl;
          if (containerEl) {
            containerEl.lastScrollTop = 0;
            containerEl.scrollTop = 0;
          }
        }
      },
    );

    const renderNoData = (): VNode | null => {
      // 未加载不显示无数据
      const { isLoaded } = c.state;
      if (isLoaded) {
        const quickToolbar = c.model.controls?.find(
          item => item.name === `${c.model.name}_quicktoolbar`,
        );
        if (quickToolbar) {
          return (
            <iBizToolbarControl
              modelData={{ ...quickToolbar, xdataControlName: c.model.name }}
              context={c.context}
              params={c.params}
              class={ns.b('quick-toolbar')}
            ></iBizToolbarControl>
          );
        }
        const noDataSlots: IParams = {};
        if (hasEmptyPanelRenderer(c)) {
          Object.assign(noDataSlots, {
            customRender: () => (
              <IBizCustomRender controller={c}></IBizCustomRender>
            ),
          });
        }
        return (
          <iBizNoData
            text={c.model.emptyText}
            emptyTextLanguageRes={c.model.emptyTextLanguageRes}
            hideNoDataImage={c.state.hideNoDataImage}
          >
            {noDataSlots}
          </iBizNoData>
        );
      }
      // 给null 表格会绘制默认的无数据
      return <div></div>;
    };

    const renderBatchToolBarSelected = () => {
      return (
        <div class={odooNs.e('batch-toolbar-selected')}>
          <div class={odooNs.em('batch-toolbar-selected', 'text')}>
            {c.state.total === c.state.selectedData.length ? '全部' : null}
            <span class={odooNs.em('batch-toolbar-selected', 'number')}>
              {c.state.selectedData.length}
            </span>
            已选取
          </div>
          {c.state.total !== c.state.selectedData.length && (
            <el-button
              class={odooNs.em('batch-toolbar-selected', 'button')}
              onClick={() => c.handleSelectAllChange(true)}
            >
              全选
              <span class={odooNs.em('batch-toolbar-selected', 'number')}>
                {c.state.total}
              </span>
            </el-button>
          )}
        </div>
      );
    };

    // 绘制批操作工具栏
    const renderBatchToolBar = (): VNode | undefined => {
      const batchToolbar = c.model.controls?.find(item => {
        return item.name === `${c.model.name!}_batchtoolbar`;
      });
      if (!batchToolbar || c.state.singleSelect) return;
      return (
        <div
          class={[
            ns.b('batch-toolbar'),
            ns.is('show', c.state.selectedData.length > 0),
          ]}
        >
          <div class={ns.b('batch-toolbar-content')}>
            {renderBatchToolBarSelected()}
            <iBizToolbarControl
              params={c.params}
              context={c.context}
              class={ns.b('batch-toolbar-items')}
              modelData={{ ...batchToolbar, xdataControlName: c.model.name }}
            ></iBizToolbarControl>
          </div>
        </div>
      );
    };

    // 绘制表格列
    const renderTableColumn = (
      model: IDEGridColumn,
      index: number,
    ): VNode | null => {
      if (slots[model.id!]) {
        return renderSlot(slots, model.id!, {
          model,
          data: c.state.items,
        });
      }
      return renderChildColumn(c, model, renderColumns.value, index);
    };

    // 绘制拖动图标列
    const renderDragIconColumn = () => {
      if (!c.enableRowEditOrder) return;
      return (
        <el-table-column
          class-name={ns.e('drag-icon')}
          type='default'
          width='16'
        >
          {{
            default: () => {
              return (
                <svg
                  viewBox='0 0 16 16'
                  class='icon'
                  xmlns='http://www.w3.org/2000/svg'
                  height='1em'
                  width='1em'
                  focusable='false'
                >
                  <g id='drag-icon/drag--' stroke-width='1' fill-rule='evenodd'>
                    <g
                      id='drag-icon'
                      transform='translate(5 1)'
                      fill-rule='nonzero'
                    >
                      <path
                        d='M1 2a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm4 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2zM1 6a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm4 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm-4 4a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm4 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm-4 4a1 1 0 1 1 0-2 1 1 0 0 1 0 2zm4 0a1 1 0 1 1 0-2 1 1 0 0 1 0 2z'
                        id='drag-icon-air'
                      ></path>
                    </g>
                  </g>
                </svg>
              );
            },
          }}
        </el-table-column>
      );
    };

    /**
     * 绘制行明细
     *
     * @return {*}
     */
    const renderRowDetail = () => {
      const { navAppViewId, navViewHeight } = c.model;
      if (navAppViewId && c.state.showRowDetail)
        return (
          <el-table-column type='expand'>
            {{
              default: ({ row }: IData): VNode | null => {
                const { context, params } = c.calcNavParams(row);
                const style = {
                  height: navViewHeight ? `${navViewHeight}px` : 'auto',
                };
                return h(resolveComponent('IBizViewShell'), {
                  style,
                  params,
                  context,
                  viewId: navAppViewId,
                  class: ns.b('row-detail-view'),
                });
              },
            }}
          </el-table-column>
        );
    };

    const loadData = async (
      group: IGroupData,
      _treeNode: IData,
      resolve: (data: IData[]) => void,
    ): Promise<void> => {
      let result: IData[] = [];
      try {
        result = await c.loadTreeData(group);
        group.children = result;
      } catch (error) {
        ibiz.log.error(error);
      } finally {
        resolve(result);
      }
    };

    const handleExpandChange = (row: any, expanded: boolean) => {
      nextTick(() => {
        if (!tableRef.value) return;
        // 计算当前折叠/展开行的业务数据
        const selectedData = [...c.state.selectedData];
        const childrenKeys: string[] = [];
        recursiveIterate({ children: [row] }, item => {
          childrenKeys.push(item.srfkey);
        });
        const children = c.state.items.filter(item =>
          childrenKeys.includes(item.srfkey),
        );
        // 根据当前行的展开状态设置选中数据
        if (expanded) {
          // 如果展开时是选中所有数据状态，则将子数据全部添加到选中数据中，
          children.forEach(item => {
            // 添加当前子数据的展开
            if (
              !c.state.expandItems.some(expand => expand.srfkey === item.srfkey)
            )
              c.state.expandItems.push(item);
            // 如果是全选数据状态，将子数据全部添加到选中数据中
            if (
              c.state.selectAll &&
              !selectedData.some(select => select.srfkey === item.srfkey)
            )
              selectedData.push(item);
          });
        } else {
          // 折叠且不是选中所有数据状态时
          children.forEach(item => {
            // 清除当前子数据的展开
            const expandIndex = c.state.expandItems.findIndex(
              expand => expand.srfkey === item.srfkey,
            );
            if (expandIndex !== -1) c.state.expandItems.splice(expandIndex, 1);
            // 如果不是全选数据状态，清除当前子数据的选中
            if (!c.state.selectAll) {
              const index = selectedData.findIndex(
                select => select.srfkey === item.srfkey,
              );
              if (index !== -1) selectedData.splice(index, 1);
            }
          });
        }
        c.setSelection(selectedData);
      });
    };

    const renderSelectionColumn = () => {
      if (c.state.singleSelect) return;
      const widthName = renderColumns.value.length ? 'width' : 'min-width';
      return (
        <el-table-column
          fixed='left'
          type='index'
          align='center'
          {...{ [widthName]: 55 }}
          class-name={odooNs.e('selection')}
        >
          {{
            header: () => {
              return (
                <el-checkbox
                  size='large'
                  class={odooNs.em('selection', 'checkbox')}
                  modelValue={isAllSelected.value}
                  onChange={() => toggleAllSelection()}
                />
              );
            },
            default: ({ row }: IData) => {
              return (
                <el-checkbox
                  size='large'
                  modelValue={isSelected(row)}
                  class={odooNs.em('selection', 'checkbox')}
                  onChange={() => toggleRowSelection(row)}
                  onClick={(e: MouseEvent) => e.stopPropagation()}
                />
              );
            },
          }}
        </el-table-column>
      );
    };

    return {
      c,
      ns,
      odooNs,
      tableRef,
      tableData,
      sysCssName,
      defaultSort,
      renderColumns,
      headerCssVars,
      infiniteScroll,
      infiniteScrollKey,
      isLodeMoreDisabled,
      loadData,
      onRowClick,
      spanMethod,
      onSortChange,
      onDbRowClick,
      onPageChange,
      renderNoData,
      summaryMethod,
      headerDragend,
      renderPopover,
      onPageRefresh,
      renderRowDetail,
      onPageSizeChange,
      renderTableColumn,
      onSelectionChange,
      handleRowClassName,
      renderBatchToolBar,
      handleExpandChange,
      renderDragIconColumn,
      renderSelectionColumn,
      handleHeaderCellClassName,
    };
  },
  render() {
    if (!this.c.state.isCreated) return;
    const state = this.c.state;
    const defaultExpandAll = this.c.controlParams.defaultexpandall === 'true';

    return (
      <iBizControlNavigation controller={this.c}>
        <iBizControlBase
          class={[
            this.ns.b(),
            this.odooNs.b(),
            this.ns.is('dynamic-grid', this.c.state.isAutoGrid),
            this.ns.is('show-header', !this.c.state.hideHeader),
            this.ns.is('enable-page', this.c.state.enablePagingBar),
            this.ns.is('enable-group', this.c.state.enableGroup),
            this.ns.is('single-select', state.singleSelect),
            this.ns.is('empty', state.items.length === 0),
            this.ns.is('enable-customized', this.c.model.enableCustomized),
          ]}
          controller={this.c}
          style={this.headerCssVars}
        >
          {
            <el-table
              border
              ref='tableRef'
              row-key='tempsrfkey'
              load={this.loadData}
              data={this.tableData}
              tooltip-effect='light'
              class={this.ns.e('table')}
              scrollbar-always-on={true}
              onRowClick={this.onRowClick}
              span-method={this.spanMethod}
              default-sort={this.defaultSort}
              lazy={this.c.state.enableGroup}
              onSortChange={this.onSortChange}
              onRowDblclick={this.onDbRowClick}
              summary-method={this.summaryMethod}
              onHeaderDragend={this.headerDragend}
              default-expand-all={defaultExpandAll}
              show-header={!this.c.state.hideHeader}
              row-class-name={this.handleRowClassName}
              onExpandChange={this.handleExpandChange}
              highlight-current-row={state.singleSelect}
              header-cell-class-name={this.handleHeaderCellClassName}
              {...this.$attrs}
              {...renderAttrs(this.c.model, {
                ...this.c.getEventArgs(),
              })}
            >
              {{
                empty: this.renderNoData,
                default: (): VNodeArrayChildren => {
                  return [
                    this.renderDragIconColumn(),
                    this.renderSelectionColumn(),
                    this.renderRowDetail(),
                    this.renderColumns.map((model, index) => {
                      return this.renderTableColumn(model, index);
                    }),
                  ];
                },
                append: () => {
                  return [
                    <div
                      ref={'infiniteScroll'}
                      key={this.infiniteScrollKey}
                      v-infinite-scroll={() => this.c.loadMore()}
                      infinite-scroll-distance={10}
                      infinite-scroll-disabled={this.isLodeMoreDisabled}
                    ></div>,
                    this.c.model.pagingMode === 3 &&
                      !(
                        this.c.state.items.length >= this.c.state.total ||
                        this.c.state.isLoading ||
                        this.c.state.total <= this.c.state.size
                      ) && (
                        <div class={this.ns.e('load-more-button')}>
                          <el-button text onClick={() => this.c.loadMore()}>
                            {ibiz.i18n.t('control.common.loadMore')}
                          </el-button>
                        </div>
                      ),
                    this.c.state.isAutoGrid ? (
                      <el-button
                        type='info'
                        class={this.ns.e('add')}
                        onClick={() => this.c.newRow()}
                      >
                        <ion-icon name='add-outline'></ion-icon>
                        {ibiz.i18n.t('app.add')}
                      </el-button>
                    ) : (
                      this.renderPopover()
                    ),
                  ];
                },
              }}
            </el-table>
          }
          {this.c.state.enablePagingBar && (
            <iBizPagination
              total={state.total}
              curPage={state.curPage}
              size={state.size}
              totalPages={state.totalPages}
              onChange={this.onPageChange}
              popperClass={`${this.sysCssName}--popper`}
              onPageSizeChange={this.onPageSizeChange}
              onPageRefresh={this.onPageRefresh}
            ></iBizPagination>
          )}
          {this.c.model.enableCustomized && !this.c.state.hideHeader && (
            <div class={this.ns.b('setting-box')}>
              <iBizGridSetting
                columnStates={state.columnStates}
                controller={this.c}
              ></iBizGridSetting>
            </div>
          )}
          {this.renderBatchToolBar()}
        </iBizControlBase>
      </iBizControlNavigation>
    );
  },
});
